//////////////////////////////////////////////////////////////////////////
//
// This file is a part of UKROP project.
// Copyright (c) Vladimir Gumenuk
//
// PROJECT:         Xort Custom Debug Engine
// FILE:            DebugStack.h
// AUTHOR:          Vladimir Gumenuk
// DATE:            4-Jan-2013
// MODIFICATIONS:
//
//////////////////////////////////////////////////////////////////////////

#pragma once

//////////////////////////////////////////////////////////////////////////
// Helper
class CFrameInfo : public FRAMEINFO
{
    DWORD m_dwRefCount;
public:
    CFrameInfo();
    CFrameInfo( FRAMEINFO& fr );
    virtual ~CFrameInfo();
    DWORD AddRef();
    DWORD Release();
    void operator= ( FRAMEINFO& fr );
    static void Zero( FRAMEINFO& fr );
    static void Copy( FRAMEINFO& frDst, FRAMEINFO& frSrc );
    static void Clear( FRAMEINFO& fr );

    void FormatFunctionArgs( UINT nRadix, Symbol *pProc, DWORD dwStackBase, SYMBOLLIST listGlobal,  XRuntime *pRT );
};


class CDebugStackCell : public xobject
{
public:
    CDebugStackCell()
    {
        m_bAllowSingleStep = TRUE;
        m_pFrameInfo     = NULL;
        m_dwStackBase    = 0;
        m_dwLastLineNo   = 0;
    }

    CDebugStackCell( Symbol* pProc, BOOL bAllowSingleStep, DWORD dwLastLineNo, DWORD dwStkBase, CFrameInfo *pFrameInfo )
    {
        m_pProc          = pProc;
        m_bAllowSingleStep = bAllowSingleStep;
        m_dwLastLineNo   = dwLastLineNo;
        m_dwStackBase    = dwStkBase;
        m_pFrameInfo     = pFrameInfo;
        m_pFrameInfo->AddRef();
    }

    virtual ~CDebugStackCell() { 
        SAFE_RELEASE( m_pFrameInfo );
    }

    IMPLEMENT_SYSTEM_XUNKNOWN();

    void FormatFunctionArgs( UINT nRadix, SYMBOLLIST listGlobal, XRuntime *pRT ) {
        m_pFrameInfo->FormatFunctionArgs( nRadix, m_pProc, m_dwStackBase, listGlobal, pRT );
    }

    void ReleaseCrossRefs();

    Symbol      *m_pProc;           // procedure that created this stack frame, weak ref
    BOOL         m_bAllowSingleStep;// this frame allows single stepping or not?
    DWORD        m_dwLastLineNo;  // actual line number in the source file
    CFrameInfo  *m_pFrameInfo;    // information about this frame
    DWORD        m_dwStackBase;   // base of the XVM stack of this information block
};

typedef CDebugStackCell* CDebugStackCellPtr;

//////////////////////////////////////////////////////////////////////////
// This helper stack duplicates the XVM execution stack and keeps information about it
class CDebugStack : 
    public xarray<CDebugStackCellPtr, CCopyObject<CDebugStackCellPtr>, CDeleteObject<CDebugStackCellPtr> >
{
public:
    virtual ~CDebugStack() { ; }

    // for simplified stack implementation
    inline CDebugStackCellPtr top() { // no AddRef!
        return m_pItems[ m_dwCount-1 ];
    }

    inline void pop() { //-ref
        m_dwCount -= 1;
        CDebugStackCellPtr p = m_pItems[ m_dwCount ];
        p->ReleaseCrossRefs();
        p->Release();
        m_pItems[ m_dwCount ] = 0;
    }

    inline CDebugStackCellPtr popNoRelease() { // caller must call Release on the returned object
        m_dwCount -= 1;
        CDebugStackCellPtr p = m_pItems[ m_dwCount ];
        m_pItems[ m_dwCount ] = 0;
        return p;
    }
    
    // returns the index of the element
    inline dword push( const CDebugStackCellPtr Item ) { // +ref
        return add( Item ) - 1; 
    }

    void ReleaseInternalXrefs() {
        for( dword i = 0; i < m_dwCount; i++ ) {
            m_pItems[i]->ReleaseCrossRefs();
        }
    }
};


typedef PROPERTIES_LIST* LPPROPLIST;
//////////////////////////////////////////////////////////////////////////
// This helper stack duplicates XVM execution stack and holds stack transition information
class CFramePropertyStack :
    public xarray<LPPROPLIST, CCopyObject<LPPROPLIST>, CDeleteObject<LPPROPLIST> >
{
public:
    virtual ~CFramePropertyStack() { ; }

    inline LPPROPLIST top() { // no AddRef!
        return m_pItems[ m_dwCount-1 ];
    }

    inline void pop() { //-ref
        m_dwCount -= 1;
        LPPROPLIST p = m_pItems[ m_dwCount ];
        p->Release();
        m_pItems[ m_dwCount ] = 0;
    }

    inline LPPROPLIST popNoRelease() { // caller must call Release on the returned object
        m_dwCount -= 1;
        LPPROPLIST p = m_pItems[ m_dwCount ];
        m_pItems[ m_dwCount ] = 0;
        return p;
    }
    
    // returns the index of the element
    inline dword push( const LPPROPLIST Item ) { // +ref
        return add( Item ) - 1; 
    }    
};
